package org.spruice.reflect;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class MethodReplay {

    private final Map<Method, Object[]> values = new HashMap<Method, Object[]>();
    private StringToValue converters;

    public MethodReplay(StringToValue converters) {
        this.converters = converters;
    }

    public void add(Method name, Object[] value) {
        values.put(name, value);
    }

    public void apply(Object instance) {
        for (Map.Entry<Method, Object[]> entry : values.entrySet()) {
            try {
                entry.getKey().invoke(instance, entry.getValue());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void tranlateProperties(Properties props, Class clzz) {
        Set<String> names = props.stringPropertyNames();
        for (String name : names) {
            String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
            Method m = findMethod(methodName, clzz);
            String strValue = props.getProperty(name);
            Object[] value = {converters.convert(m.getParameterTypes()[0], strValue)};
            values.put(m, value);
        }
    }

    private Method findMethod(String methodName, Class clzz) {
        Method[] methods = clzz.getMethods();
        Method result = null;
        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.getParameterTypes().length == 1) {
                if (result != null) {
                    throw new RuntimeException("Ambiguous Property Method: " + methodName);
                }
                result = method;
            }
        }
        if (result == null)
            throw new RuntimeException("Unable to find method: " + methodName);
        result.setAccessible(true);
        return result;
    }
}
